package api

import (
	"net/http"
	"os"

	"example.com/m/v2/config"
	"example.com/m/v2/model"
	"example.com/m/v2/service"
	"example.com/m/v2/types"
	"github.com/gin-gonic/gin"
	"gopkg.in/yaml.v2"
)

type App struct {
	svcCtx *service.ServiceContext
}

func Init() {
	// gin
	r := gin.Default()

	// config
	data, err := os.ReadFile("./etc/config.yaml")
	if err != nil {
		panic("config error")
	}
	config := config.Config{}
	err = yaml.Unmarshal(data, &config)
	if err != nil {
		panic("config error")
	}

	// db
	db, err := model.Init(&config)
	if err != nil {
		panic("db error")
	}

	// services
	ctx := service.ServiceContext{}
	ctx.Token = service.NewToken(&ctx, config.JwtSecret)
	ctx.Cate = service.NewCate(&ctx, db)
	ctx.Imgs = service.NewImgs(&ctx, db)
	ctx.Image = service.NewImage(&ctx, db)
	ctx.Material = service.NewMaterial(&ctx, db)
	ctx.Poster = service.NewPoster(&ctx, db)
	ctx.Template = service.NewTemplate(&ctx, db)
	ctx.User = service.NewUser(&ctx, db)
	ctx.Fonts = service.NewFonts(&ctx, db)

	api := App{
		svcCtx: &ctx,
	}

	api.SetupRouter(r)

	// Listen and Server in 0.0.0.0:8080
	r.Run(":" + config.Port)
}

func (api *App) SetupRouter(r *gin.Engine) *gin.Engine {
	// Disable Console Color
	// gin.DisableConsoleColor()
	// r := gin.Default()

	// Ping test
	r.GET("/ping", func(c *gin.Context) {
		c.String(http.StatusOK, "pong")
	})

	r.OPTIONS("/design/cate", func(c *gin.Context) {
		c.Header("Allow", "GET, POST, OPTIONS")
		c.Status(204) // 204 表示无内容
	})
	r.GET("/design/cate", func(c *gin.Context) {
		var req types.DesignCateReq
		c.Bind(&req)

		res := api.DesignCate(&req)
		c.JSON(http.StatusOK, res)
	})

	r.OPTIONS("/design/imgs", func(c *gin.Context) {
		c.Header("Allow", "GET, POST, OPTIONS")
		c.Status(204) // 204 表示无内容
	})
	r.GET("/design/imgs", func(c *gin.Context) {
		var req types.DesignImgsReq
		c.Bind(&req)

		res := api.DesignImgs(&req)
		c.JSON(http.StatusOK, res)
	})

	r.OPTIONS("/design/list", func(c *gin.Context) {
		c.Header("Allow", "GET, POST, OPTIONS")
		c.Status(204) // 204 表示无内容
	})
	r.GET("/design/list", func(c *gin.Context) {
		var req types.DesignListReq
		c.Bind(&req)

		res := api.DesignList(&req)
		c.JSON(http.StatusOK, res)
	})

	r.OPTIONS("/design/material", func(c *gin.Context) {
		c.Header("Allow", "GET, POST, OPTIONS")
		c.Status(204) // 204 表示无内容
	})
	r.GET("/design/material", func(c *gin.Context) {
		var req types.DesignMaterialReq
		c.Bind(&req)

		res := api.DesignMaterial(&req)
		c.JSON(http.StatusOK, res)
	})

	r.OPTIONS("/design/temp", func(c *gin.Context) {
		c.Header("Allow", "GET, POST, OPTIONS")
		c.Status(204) // 204 表示无内容
	})
	r.GET("/design/temp", func(c *gin.Context) {
		var req types.DesignTempReq
		c.Bind(&req)

		res := api.DesignTemp(&req)
		c.JSON(http.StatusOK, res)
	})

	r.OPTIONS("/design/fonts", func(c *gin.Context) {
		c.Header("Allow", "GET, POST, OPTIONS")
		c.Status(204) // 204 表示无内容
	})
	r.GET("/design/fonts", func(c *gin.Context) {
		var req types.DesignFontsReq
		c.Bind(&req)

		res := api.DesignFonts(&req)
		c.JSON(http.StatusOK, res)
	})

	r.OPTIONS("/login", func(c *gin.Context) {
		c.Header("Allow", "GET, POST, OPTIONS")
		c.Status(204) // 204 表示无内容
	})
	r.POST("/login", func(c *gin.Context) {
		var req types.LoginReq
		c.Bind(&req)

		res := api.Login(&req)
		c.JSON(http.StatusOK, res)
	})

	//----------------------------------------------------------------
	// need login

	/*
		authorized := r.Group("/", gin.BasicAuth(gin.Accounts{
			"foo":  "bar", // user:foo password:bar
			"manu": "123", // user:manu password:123
		}))
	*/

	authorized := r.Group("/", gin.HandlerFunc(func(c *gin.Context) {
		tokenString := c.GetHeader("Authorization")
		if tokenString == "" {
			c.AbortWithStatusJSON(401, gin.H{"message": "未授权"})
			return
		}
		claims, err := api.svcCtx.Token.Parse(tokenString)
		if err != nil {
			c.AbortWithStatusJSON(401, gin.H{"message": "无效的令牌"})
			return
		}
		if claims["userId"] == nil {
			c.AbortWithStatusJSON(401, gin.H{"message": "无效的令牌"})
			return
		}
		userId := int64(claims["userId"].(float64))
		c.Set("userId", userId)
	}))

	/* example curl for /admin with basicauth header
	   Zm9vOmJhcg== is base64("foo:bar")

		curl -X POST \
	  	http://localhost:8080/admin \
	  	-H 'authorization: Basic Zm9vOmJhcg==' \
	  	-H 'content-type: application/json' \
	  	-d '{"value":"bar"}'
	*/
	r.OPTIONS("/design/user/image", func(c *gin.Context) {
		c.Header("Allow", "GET, POST, OPTIONS")
		c.Status(204) // 204 表示无内容
	})
	authorized.GET("/design/user/image", func(c *gin.Context) {
		//user := c.MustGet(gin.AuthUserKey).(string)
		userId := c.MustGet("userId").(int64)

		var req types.UserImageReq
		c.Bind(&req)

		res := api.UserImage(&req, userId)
		c.JSON(http.StatusOK, res)
	})

	r.OPTIONS("/design/user/add_image", func(c *gin.Context) {
		c.Header("Allow", "GET, POST, OPTIONS")
		c.Status(204) // 204 表示无内容
	})
	authorized.POST("/design/user/add_image", func(c *gin.Context) {
		//user := c.MustGet(gin.AuthUserKey).(string)
		userId := c.MustGet("userId").(int64)

		var req types.AddImageReq
		c.Bind(&req)

		res := api.AddImage(&req, userId)
		c.JSON(http.StatusOK, res)
	})

	authorized.POST("/image/del", func(c *gin.Context) {
		//user := c.MustGet(gin.AuthUserKey).(string)
		userId := c.MustGet("userId").(int64)

		var req types.PosterDelReq
		c.Bind(&req)

		res := api.PosterDel(&req, userId)
		c.JSON(http.StatusOK, res)
	})

	authorized.GET("/design/my", func(c *gin.Context) {
		//user := c.MustGet(gin.AuthUserKey).(string)
		userId := c.MustGet("userId").(int64)

		var req types.DesignMyReq
		c.Bind(&req)

		res := api.DesignMy(&req, userId)
		c.JSON(http.StatusOK, res)
	})

	authorized.GET("/design/poster", func(c *gin.Context) {
		//user := c.MustGet(gin.AuthUserKey).(string)
		userId := c.MustGet("userId").(int64)

		var req types.DesignPosterReq
		c.Bind(&req)

		res := api.DesignPoster(&req, userId)
		c.JSON(http.StatusOK, res)
	})

	authorized.POST("/design/save", func(c *gin.Context) {
		//user := c.MustGet(gin.AuthUserKey).(string)
		userId := c.MustGet("userId").(int64)

		var req types.DesignSaveReq
		c.Bind(&req)

		res := api.DesignSave(&req, userId)
		c.JSON(http.StatusOK, res)
	})

	authorized.POST("/poster/del", func(c *gin.Context) {
		//user := c.MustGet(gin.AuthUserKey).(string)
		userId := c.MustGet("userId").(int64)

		var req types.PosterDelReq
		c.Bind(&req)

		res := api.PosterDel(&req, userId)
		c.JSON(http.StatusOK, res)
	})

	return r
}
